package cn.dapeng.interview;

/**
 * 题目：写一个固定容量的同步容器，拥有put和get方法，以及getCount方法
 * 能够支持2个生产者和10个消费者的阻塞调用
 * <p>
 * 使用ReentrantLock和Condition来实现
 * <p>
 * <p>
 * 同步容器：多线程往里装，多线程往外取
 */

import lombok.Getter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class SynchronizeContainer_T02<T> {

    private final List<T> list = new ArrayList<T>();

    private final static int MAX_SIZE = 10;

    @Getter
    private int count = 0;

    private final ReentrantLock lock = new ReentrantLock();
    /**
     * condition相当于一个等待队列，这里new了两个condition对象，就创建了两个等待队列
     *
     * 当调用await方法时相当于把当前线程放入等待队里，当调用signalAll相当于唤醒当前等待队列所有线程，去争抢执行机会
     */
    private final Condition producer = lock.newCondition();
    private final Condition consumer = lock.newCondition();


    public void put(T t) throws InterruptedException {
        try {
            lock.lock();
            while (count == MAX_SIZE) {
                producer.await();
            }

            list.add(t);
            count++;
            consumer.signalAll();
        } finally {
            lock.unlock();
        }
    }


    public T get() throws InterruptedException {
        try {
            lock.lock();
            while (count == 0) {
                consumer.await();
            }

            T t = list.remove(0);
            count--;
            producer.signalAll();
            return t;

        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws IOException {
        SynchronizeContainer_T02<Integer> object = new SynchronizeContainer_T02<>();
        for (int i = 0; i < 2; i++) {
            final int o = i;
            new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    try {
                        object.put(o);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName() + " put :: " + o);
                }
            }, "t" + i).start();
        }

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (true) {
                    try {
                        System.out.println(Thread.currentThread().getName() + " get :: " + object.get());
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }, "T" + i).start();
        }

        System.in.read();
    }


}
